package org.example.controller.process;


import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskInfo;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.example.util.FlowUtil;
import org.example.util.XMLUtil;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;

@RequestMapping("/proc")
@Controller
@Slf4j
public class ProcessController {
    @Resource
    private RepositoryService repositoryService;

    @Resource
    private HistoryService historyService;

    @Resource
    private RuntimeService runtimeService;

    @Resource
    private TaskService taskService;

    @Resource
    private XMLUtil xmlUtil;

    @Resource
    private FlowUtil flowUtil;

    /**
     * 查看当前用户发起的流程，不包括被发起人撤回（删除）的流程
     */
    @RequestMapping("/list")
    @ResponseBody
    public List getProc(){
        List<HistoricProcessInstance> list = historyService.createHistoricProcessInstanceQuery().startedBy("JH050178")
                .notDeleted()
                .list();
        return list;
    }


    /**
     * 后端直接生成流程图供前端使用
     * @param id
     * @param response
     * @throws IOException
     */
    @RequestMapping("/getPicById")
    public void getPictureById(String id, HttpServletResponse response) throws IOException {
//        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
//                .processInstanceId("75001").singleResult();
        //流程实例查询
        ProcessInstance pi = runtimeService // 获取运行时Service
                .createProcessInstanceQuery() // 创建流程实例查询
                .processInstanceId("62501") // 用流程实例id查询
                .singleResult();
        HistoricProcessInstance instance = historyService.createHistoricProcessInstanceQuery().processInstanceId(id)
                .singleResult();
        //获取流程定义id act_re_procdef 的 def 指 definition
        String definitionId = instance.getProcessDefinitionId();
        //获取model
        BpmnModel bpmnModel = repositoryService.getBpmnModel(definitionId);
        //获取高亮节点
        List<String> activeActivityIds = new ArrayList<>();
        List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(instance.getId()).list();
        for(HistoricActivityInstance e : historicActivityInstanceList){
            activeActivityIds.add(e.getActivityId());
        }
        //高亮连线集合
        ProcessDefinitionEntity processDefinitionEntity=(ProcessDefinitionEntity)repositoryService.getProcessDefinition(instance.getProcessDefinitionId());
        //获取流程已经发生流转的线Id集合
        List<String> lightFlow = this.getHighLightedFlows(processDefinitionEntity,historicActivityInstanceList);
        //以上数据获取完毕，开始绘图
        String activityFontName = "宋体"; // 节点字体
        String labelFontName = "微软雅黑"; // 连线标签字体
        String annotationFontName = "宋体"; // 连线标签字体
        ClassLoader customClassLoader = null; // 类加载器
        double scaleFactor = 1.0d; // 比例因子，默认即可
        boolean drawSequenceFlowNameWithNoLabelDI = true; // 不设置连线标签不会画
        //以上数据获取完毕，开始绘图
        DefaultProcessDiagramGenerator defaultProcessDiagramGenerator = new DefaultProcessDiagramGenerator();
        InputStream inputStream = defaultProcessDiagramGenerator.generateDiagram(bpmnModel, "png", activeActivityIds, lightFlow,
                activityFontName,labelFontName,annotationFontName,customClassLoader,scaleFactor);
        ServletOutputStream out = response.getOutputStream();
        // 读取文件流
        int len = 0;
        byte[] buffer = new byte[1024 * 10];
        while ((len = inputStream.read(buffer)) != -1) {
            out.write(buffer, 0, len);
        }
        out.flush();
    }

    /**
     * 获取高亮连线结合
     * @param processDefinitionEntity 流程定义实体
     * @param historicActivityInstances 历史节点
     * @return
     */
    public List<String> getHighLightedFlows(ProcessDefinitionEntity processDefinitionEntity,
                                            List<HistoricActivityInstance> historicActivityInstances){
        List<String> highLightedFlows=new ArrayList<>();

        for(int i=0;i<historicActivityInstances.size()-1;i++) {
            //获取节点的详细信息
            ActivityImpl activityImpl=processDefinitionEntity.findActivity(historicActivityInstances.get(i).getActivityId());
            List<ActivityImpl> sameStartTimeNodes=new ArrayList<>();//保存后续开始时间一样的节点
            ActivityImpl sameActivityImpl=processDefinitionEntity.findActivity(historicActivityInstances.get(i+1).getActivityId());
            //将后续第一个节点放在时间相同节点的集合里
            sameStartTimeNodes.add(sameActivityImpl);
            for(int j=i+1;j<historicActivityInstances.size()-1;j++) {
                HistoricActivityInstance activityImpl1=historicActivityInstances.get(j);
                HistoricActivityInstance activityImpl2=historicActivityInstances.get(j+1);
                if(Math.abs(activityImpl1.getStartTime().getTime()-activityImpl2.getStartTime().getTime())<200) {
                    //if(activityImpl1.getStartTime().equals(activityImpl2.getStartTime())) {
                    //如果第一个节点和第二个节点开始时间相同保存
                    ActivityImpl sameActivityImpl2=processDefinitionEntity.findActivity(activityImpl2.getActivityId());
                    sameStartTimeNodes.add(sameActivityImpl2);
                }else {
                    //不同就跳出循环
                    break;
                }
            }
            List<PvmTransition> pvmTransitions=activityImpl.getOutgoingTransitions();
            for(PvmTransition pvmTransition:pvmTransitions) {
                //对所有的线进行遍历
                ActivityImpl pvmActivityImpl=(ActivityImpl)pvmTransition.getDestination();
                //如果取出来的目标节点存在时间相同的节点里，保存该线的id,进行高亮显示
                if(sameStartTimeNodes.contains(pvmActivityImpl)) {

                    highLightedFlows.add(pvmTransition.getId());
                }
            }

        }
        return highLightedFlows;
    }


    /**
     * 后端生成xml，前端解析xml生成流程图
     * @param id
     * @return
     */
    @RequestMapping("/getPicById2")
    @ResponseBody
    public Map<String,?> getPictureById2(String id) throws DocumentException {
        Map<String,Object> map = new HashMap<>();
        HistoricProcessInstance instance = historyService.createHistoricProcessInstanceQuery().processInstanceId(id)
                .singleResult();
        String procDefId = instance.getProcessDefinitionId();
        String mxGraphXxml = xmlUtil.convertXml(procDefId);
        map.put("mxGraphXml",mxGraphXxml);
        ProcessInstance pi= runtimeService // 获取运行时Service
                .createProcessInstanceQuery() // 创建流程实例查询
                .processInstanceId(id) // 用流程实例id查询
                .singleResult();
        List<String> currentIds = new ArrayList<>();
        if(pi != null ) {
            //当前流程实例未结束，获取当前节点id
            currentIds.add(pi.getActivityId());
        }else{
            //如果当前流程实例已结束，设为结束节点
             //获取历史流程实例
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                    .processInstanceId(id).singleResult();
            currentIds.add(historicProcessInstance.getEndActivityId());
        }
        map.put("currentIds",currentIds);

        //高亮连线集合
        ProcessDefinitionEntity processDefinitionEntity=(ProcessDefinitionEntity)repositoryService.getProcessDefinition(instance.getProcessDefinitionId());
        //获取已经走过的节点，包括当前节点
        List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(instance.getId()).list();
        //获取流程已经发生流转的线Id集合
        List<String> lightFlow = this.getHighLightedFlows(processDefinitionEntity,historicActivityInstanceList);
        map.put("flowedEdgeIds", lightFlow);
        return map;
    }

    /**
     * 根据节点id和流程实例id获取当前节点的代理人
     * @return
     */
    @RequestMapping("/getAssigneeByCondition")
    @ResponseBody
    public String getAssigneeById(String nodeId, String procInsId){
        String assignee = null;
        HistoricActivityInstance historicActivityInstance = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(procInsId).activityId(nodeId).singleResult();
        if(historicActivityInstance != null ) {
            if ("startEvent".equals(historicActivityInstance.getActivityType()) || "endEvent".equals(historicActivityInstance.getActivityType())) {
                HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                        .processInstanceId(procInsId).singleResult();
                assignee = historicProcessInstance.getStartUserId();
            } else {
                assignee = historicActivityInstance.getAssignee();
            }
        }
        return assignee;
    }

    @RequestMapping("/deploy")
    @ResponseBody
    public String deploy(String id)  {
        String message = null;
        try {
            // 获取模型
            Model model = repositoryService.getModel(id);
            ObjectNode objectNode = (ObjectNode) new ObjectMapper().readTree(repositoryService.getModelEditorSource(model.getId()));
            BpmnModel bpmnModel = new BpmnJsonConverter().convertToBpmnModel(objectNode);

            String processName = model.getName() + ".bpmn20.xml";
            byte[] bytes = new BpmnXMLConverter().convertToXML(bpmnModel);
            // 部署流程
            Deployment deployment = repositoryService
                    .createDeployment().name(model.getName())
                    .addString(processName, new String(bytes, "UTF-8"))
                    .deploy();
            message = "部署成功";
        } catch (IOException e) {
            e.printStackTrace();
            message = "部署失败";
        }
        return message;
    }

    /**
     * 根据流程实例id撤回流程
     * 撤回流程时针对发起人的，就是删除，
     * @param id
     * @return
     */
    @RequestMapping("/withdraw")
    @ResponseBody
    public String withdraw(String id)  {
        String message = null;
        try {
            //下面的方法只会删除 act_ru_*的数据，并不会删除 act_hi_*的数据，但在历史流程实例表中，该流程已被置为完成
            runtimeService.deleteProcessInstance(id,"发起人撤回流程");
            message = "撤回成功";
        } catch (Exception e) {
            e.printStackTrace();
            message = "撤回失败";
        }
        return message;
    }








}
